package com.sgzn.sgpay.service.impl;

import com.sgzn.sgpay.enums.OrderStateEnum;
import com.sgzn.sgpay.enums.OrderTypeEnum;
import com.sgzn.sgpay.enums.SeqFlagEnum;
import com.sgzn.sgpay.enums.SgsResponseCode;
import com.sgzn.sgpay.exception.SgpayException;
import com.sgzn.sgpay.model.request.SgsDepositRequest;
import com.sgzn.sgpay.model.request.WXUnifiedorderReqDto;
import com.sgzn.sgpay.model.response.SgsResultResponse;
import com.sgzn.sgpay.persistence.dao.TMainOrderMapper;
import com.sgzn.sgpay.persistence.dao.TPayOrderMapper;
import com.sgzn.sgpay.persistence.domain.TMainOrder;
import com.sgzn.sgpay.persistence.domain.TPayOrder;
import com.sgzn.sgpay.service.SgsDepositService;
import com.sgzn.sgpay.util.AmountTransUtil;
import com.sgzn.sgpay.util.GenerateSignUtils;
import com.sgzn.sgpay.util.MessageUtil;
import com.sgzn.sgpay.util.SeqUtil;
import com.sgzn.sgpay.util.httpClient.HttpXMLUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.io.UnsupportedEncodingException;
import java.math.BigDecimal;
import java.util.Date;
import java.util.Map;
import java.util.SortedMap;
import java.util.TreeMap;

/**
 * 充值服务接口实现类
 * @author huangsenguang
 */
@Slf4j
@Service
@Transactional
public class SgsDepositServiceImpl implements SgsDepositService {
    @Autowired
    private TMainOrderMapper tMainOrderMapper;
    @Autowired
    private TPayOrderMapper tPayOrderMapper;

    @Override
    public SgsResultResponse<Map<String, String>> deposit(SgsDepositRequest sgsDepositRequest) {
        SgsResultResponse sgsResponse = new SgsResultResponse();
        log.info("SgsDepositServiceImpl 充值Start");
        try {
            log.info("[{}]微信 统一下单 接口调用",sgsDepositRequest.getInitiationID());
            String userId = sgsDepositRequest.getUserId();
            //接受参数(openid)  通过loginToken 查询数据库获取openid
            WXUnifiedorderReqDto wxReqDto = assembleWxUnifiedorderReqDto(sgsDepositRequest, sgsDepositRequest.getOpenId());//组装微信支付统一下单请求类

            //预支付下单（主单、支付单）
            //下主订单
            TMainOrder tMainOrder = insertMainOrder(sgsDepositRequest, userId, wxReqDto);
            //下支付单
            TPayOrder tPayOrder = insertPayOrder(sgsDepositRequest, userId, wxReqDto);

            wxReqDto.setSign(generateSign(wxReqDto));//生成签名并设置到WXUnifiedorderReqDto
            log.info("获取签名后的WXUnifiedorderReqDto:{}", wxReqDto.toString());

            String receiveStr = getWXResp(wxReqDto);//调用微信统一下单
            log.info("预支付统一下单接口响应报文receive Content:{} ",receiveStr);

            if (StringUtils.isBlank(receiveStr)) {
                sgsResponse.setErrorCode(SgsResponseCode.C503000.getResponseCode());
                sgsResponse.setErrorMsg(SgsResponseCode.C503000.getResponseDesc());
                return sgsResponse;
            }
            //把接收到的XML格式的信息转成Map
            Map<String,String> receiveMap = MessageUtil.parseXML(receiveStr);
            log.info("receiveMap={}",receiveMap);

            //生成签文
            String checkSign = GenerateSignUtils.generateReturnSign(receiveMap, wxReqDto.getMchKey());
            //验签
            if (!checkSign.equals(receiveMap.get("sign"))) {//验签失败
                sgsResponse.setErrorCode(SgsResponseCode.C10007.getResponseCode());
                sgsResponse.setErrorMsg(SgsResponseCode.C10007.getResponseDesc());
                log.error("Sign Verify Fail：checkSign={}, validSign={}",checkSign,receiveMap.get("sign"));
                return sgsResponse;
            }

            String prepay_id = "";
            String return_code = receiveMap.get("return_code");
            String result_code = receiveMap.get("result_code");
            if ("SUCCESS".equals(return_code) && "SUCCESS".equals(result_code)){
                prepay_id = receiveMap.get("prepay_id");
            } else {
                sgsResponse.setErrorCode(SgsResponseCode.C10013.getResponseCode());
                sgsResponse.setErrorMsg(SgsResponseCode.C10013.getResponseDesc());
                log.error("prepay_id is null：{}",prepay_id);
                return sgsResponse;
            }
            //更新主订单 和 支付单
            updateOrder(tMainOrder, tPayOrder, prepay_id);

            //调用再次签名
            Map<String, String> stringStringMap = HttpXMLUtil.generateSignature(prepay_id, wxReqDto.getMchKey(), wxReqDto.getAppid(), wxReqDto.getNonce_str(), wxReqDto.getOut_trade_no());
            sgsResponse.setResult(stringStringMap);
            sgsResponse.setErrorCode(SgsResponseCode.C200.getResponseCode());
            sgsResponse.setErrorMsg(SgsResponseCode.C200.getResponseDesc());
        } catch (SgpayException e) {
            log.error("微信 统一下单 业务异常：", e);
            sgsResponse.setErrorCode(e.getCode());
            sgsResponse.setErrorMsg(e.getDesc());
        } catch (RuntimeException e) {
            log.error("微信 统一下单 异常Exception：", e);
            sgsResponse.setErrorCode(SgsResponseCode.C503000.getResponseCode());
            sgsResponse.setErrorMsg(SgsResponseCode.C503000.getResponseDesc());
        } catch (Exception e) {
            log.error("微信 统一下单 异常Exception：", e);
            sgsResponse.setErrorCode(SgsResponseCode.C503.getResponseCode());
            sgsResponse.setErrorMsg(SgsResponseCode.C503.getResponseDesc());
        }finally {
        }
        return sgsResponse;
    }

    private WXUnifiedorderReqDto assembleWxUnifiedorderReqDto(SgsDepositRequest sgsDepositRequest, String openId) throws UnsupportedEncodingException {
        //接受参数(金额)
        BigDecimal trsAmount = new BigDecimal(sgsDepositRequest.getAmount());
        log.info("小程序传过来的金额数据:{}",trsAmount.toString());
        String total_fee = AmountTransUtil.yuanToCents(trsAmount, 0);//元转分
        log.info("转换后的金额:{}",total_fee);
        WXUnifiedorderReqDto wxReqDto = new WXUnifiedorderReqDto();
        wxReqDto.setTotal_fee(total_fee);//元转分
        wxReqDto.setOpenid(openId);
        //设置body变量 (支付成功显示在微信支付 商品详情中)
        wxReqDto.setBody(new String("sgpay-pay".getBytes("ISO-8859-1"),"UTF-8"));
        log.info("充值请求转换对象:{}",wxReqDto.toString());
        return wxReqDto;
    }

    private void updateOrder(TMainOrder tMainOrder, TPayOrder tPayOrder, String prepay_id) {
        //跟新主订单
        tMainOrder.setOrderState(OrderStateEnum.SM_AFT.getCode());
        tMainOrder.setUpdatedAt(new Date());
        this.tMainOrderMapper.updateByPrimaryKeySelective(tMainOrder);

        //更新支付单
        tPayOrder.setPayState(OrderStateEnum.SM_AFT.getCode());
        tPayOrder.setPrepayId(prepay_id);
        this.tPayOrderMapper.updateByPrimaryKeySelective(tPayOrder);
    }

    private TPayOrder insertPayOrder(SgsDepositRequest sgsDepositRequest, String userId, WXUnifiedorderReqDto wxReqDto) {
        TPayOrder tPayOrder = new TPayOrder();
        tPayOrder.setOrderId(wxReqDto.getOut_trade_no());
        tPayOrder.setUserId(userId);
        tPayOrder.setAppid(wxReqDto.getAppid());
        tPayOrder.setPayOrderId(SeqUtil.getShortSeq(SeqFlagEnum.T_PAY_ORDER_ID.getCode()));
        tPayOrder.setOpenid(wxReqDto.getOpenid());
        tPayOrder.setPayState(OrderStateEnum.SM_INI.getCode());
        tPayOrder.setPayType(OrderTypeEnum.PAY.getCode());
        tPayOrder.setPayAmount(new BigDecimal(sgsDepositRequest.getAmount()));
        tPayOrder.setPayChannel("00");
        tPayOrder.setCreatedAt(sgsDepositRequest.getReqTime());
        this.tPayOrderMapper.insertSelective(tPayOrder);
        return tPayOrder;
    }

    private TMainOrder insertMainOrder(SgsDepositRequest sgsDepositRequest, String userId, WXUnifiedorderReqDto wxReqDto) {
        TMainOrder tMainOrder = new TMainOrder();
        tMainOrder.setOrderId(wxReqDto.getOut_trade_no());
        tMainOrder.setOrderState(OrderStateEnum.SM_INI.getCode());
        tMainOrder.setOrderType(OrderTypeEnum.PAY.getCode());
        tMainOrder.setUserId(userId);
        tMainOrder.setOrderAmount(new BigDecimal(sgsDepositRequest.getAmount()));
        tMainOrder.setCreatedAt(sgsDepositRequest.getReqTime());
        this.tMainOrderMapper.insertSelective(tMainOrder);
        return tMainOrder;
    }

    private String getWXResp(WXUnifiedorderReqDto wxReqDto) {
        StringBuffer paramBuffer = new StringBuffer();;
        //将参数 编写XML格式
        paramBuffer.append("<xml>");
        paramBuffer.append("<appid><![CDATA[" + wxReqDto.getAppid() + "]]></appid>");
        paramBuffer.append("<mch_id><![CDATA[" + wxReqDto.getMch_id() + "]]></mch_id>");
        paramBuffer.append("<nonce_str><![CDATA[" + wxReqDto.getNonce_str() + "]]></nonce_str>");
        paramBuffer.append("<sign><![CDATA[" + wxReqDto.getSign() + "]]></sign>");
        paramBuffer.append("<body><![CDATA[" + wxReqDto.getBody() + "]]></body>");
        paramBuffer.append("<out_trade_no><![CDATA[" + wxReqDto.getOut_trade_no() + "]]></out_trade_no>");
        paramBuffer.append("<total_fee><![CDATA[" + wxReqDto.getTotal_fee() + "]]></total_fee>");
        paramBuffer.append("<spbill_create_ip><![CDATA[" + wxReqDto.getSpbill_create_ip() + "]]></spbill_create_ip>");
        paramBuffer.append("<notify_url><![CDATA[" + wxReqDto.getNotify_url() + "]]></notify_url>");
        paramBuffer.append("<trade_type><![CDATA[" + wxReqDto.getTrade_type() + "]]></trade_type>");
        paramBuffer.append("<openid><![CDATA[" + wxReqDto.getOpenid() + "]]></openid>");
        paramBuffer.append("</xml>");
        log.info(" Send WX Content:", paramBuffer.toString());
        return HttpXMLUtil.getRemotePortData(wxReqDto.getWxUri(), new String(paramBuffer.toString().getBytes()));
    }

    private String generateSign(WXUnifiedorderReqDto wxReqDto) {
        SortedMap<String, String> paraMap = new TreeMap<String, String>();
        //设置请求参数(小程序ID)
        paraMap.put("appid", wxReqDto.getAppid());
        //设置请求参数(商户号)
        paraMap.put("mch_id", wxReqDto.getMch_id());
        //设置请求参数(随机字符串)
        paraMap.put("nonce_str", wxReqDto.getNonce_str());
        //设置请求参数(商品描述)
        paraMap.put("body", wxReqDto.getBody());
        //设置请求参数(商户订单号)
        paraMap.put("out_trade_no", wxReqDto.getOut_trade_no());
        //设置请求参数(总金额)
        paraMap.put("total_fee", wxReqDto.getTotal_fee());
        //设置请求参数(终端IP)
        paraMap.put("spbill_create_ip", wxReqDto.getSpbill_create_ip());
        //设置请求参数(通知地址)
        paraMap.put("notify_url", wxReqDto.getNotify_url());
        //设置请求参数(交易类型)
        paraMap.put("trade_type", wxReqDto.getTrade_type());
        //设置请求参数(openid)(在接口文档中 该参数 是否必填项 但是一定要注意 如果交易类型设置成'JSAPI'则必须传入openid)
        paraMap.put("openid", wxReqDto.getOpenid());
        return GenerateSignUtils.generateSign(paraMap, wxReqDto.getMchKey());
    }


}